﻿using FineCollection.Intf;




namespace FineCollection.Cont
{
    /// <summary>
    /// Hash map.
    /// </summary>
    /// <typeparam name="K">type of a key.</typeparam>
    /// <typeparam name="V">type of an assicated value.</typeparam>
    public class HashMap<K,V> : HashTable<MapEntry<K,V>>,
                                WMap<K,V>
        where K : System.IEquatable<K>
    {

        /// <summary>
        /// Constructs a map with the specified capacity.
        /// </summary>
        /// <param name="capacity"></param>
        public HashMap(int capacity) 
            : base(capacity)
        {}

        /// <summary>
        /// Trivial constructor.
        /// </summary>
        public HashMap()
        {}


        /// <summary>
        /// Looks for the pair with the specified key.
        /// </summary>
        /// <param name="key">key to look for.</param>
        /// <param name="notFound">value that should be returned when the specified key is not found.</param>
        /// <returns>value of the foun pair, or the <c>notFound</c> when the key is not found.</returns>
        public V Find (K key, V notFound)
        {
            HashChain<MapEntry<K, V>> hchain =
                FindFirst(key.GetHashCode(),
                          mapEntry => mapEntry.Key.Equals(key));
            if (hchain.chain != null)
                return hchain.chain.item.Value;
            else
                return notFound;
        }


        public V this [K key]
        {
            get 
            {
                return Find(key, default(V));
            }

            set
            {
                HashChain<MapEntry<K,V>> hchain =
                    FindFirst(key.GetHashCode(),
                              mapEntry => mapEntry.Key.Equals(key));
                if (hchain.chain != null)
                    hchain.chain.item.Value = value;
                else
                    InternalAdd(new MapEntry<K, V>(key, value));
            }
        }


        public bool ContainsKey(K key)
        {
            int hashCode = key.GetHashCode();
            bool found;
            CheckExistance(hashCode, 
                           mapEntry => mapEntry.Key.Equals(key),
                           out found);
            return found;
        }


        public void Include(K key, V value)
        {
            if (ContainsKey(key))
                throw new DuplicatesFoundException("The key <"+key+"> already exists in the map.");

            MapEntry<K,V> entry = new MapEntry<K,V>(key,value);
            InternalAdd(entry);
        }


        public V Exclude(K key)
        {
            HashChain<MapEntry<K,V>> hchain =
                    FindFirst(key.GetHashCode(),
                          mapEntry => mapEntry.Key.Equals(key));

            if (hchain.chain != null)
            {
                Remove(hchain);
                return hchain.chain.item.Value;
            }
            else
            {
                return default(V);
            }
        }


        private void InternalAdd(MapEntry<K,V> entry)
        {
            Add(entry, entry.GetHashCode());
        }
    }





}